# always clean up R environment
rm(list = ls())
# load all packages here
# Basic Data Analysis & Wrangling
library(tidyverse)
library(lubridate)
# Library for splitting the chinese words. 
library(jiebaRD)
library(jiebaR)
# Library for generating word cloud. 
library(wordcloud)
# Library for visualizing the 3d plot. 
library(plotly)

I - Introduction

Based on definition from Investopedia.com, Social good means “something that benefits the largest number of people in the largest possible way, such as clean air, clean water, healthcare and literacy.” Social good is also referred to as the “common good.” In our topic, we hope to discuss a heated issue within the community of developers. Before we go in depth of the issue, here are a couple things indicating why it’s an important issue:

Overall Chinese Internet environment:

  • A couple companies have tried to enter the lucrative market of mainland China, however, many of them faced obstacles. A famous example is from Google, who specifically designed the dragonfly plan for Chinese Market, that failed due to regulation as well as lack of interest from the general public. eBay was one of the earliest counterparts that laid eyes on Asia market, and all of these big companies failed for the same reason: they all had unsuccessful integration of incompatible cultures. We personally call it “attempt of cultural imperialism within the field of internet.” The big companies took over many places with success without changing much of its business model, or approach of the local audience. However, there is a tremendous culture difference between western and eastern society. One example is the young generate in the United States use Facebook, Twitter, Instagram, Snapchat and other social platforms simultaneously. Teens in China all only use one social media app, Wechat, that could also pay for bills, call an uber, book a movie, find the restaurant. Consumer behavior and culture difference is what pulled these big companies back.

  • Chinese government is known for its regulation and censorship. According to an article published by New York Times, the new president hopes to use the Internet to strengthen Communist Party’s role on the society. Majority of the young generation is indifferent to politics, although many are victims of censorship as well as censorship factory workers.

From a brief introduction of how the overall Chinese Internet environment is different from the United States, and other developed countries, we will now connect to the topic of interest today: 996.ICU event.

This movement is personally significant to our team, because both of us have interacted with the companies mentioned above and have friends and families who work in Tech in China. We have witnessed the consequences caused long hours, and unproductive work in the Tech industry in China. On one hand, the Chinese overall Internet environment is different from the United States, as well as it is at least 10 years behind the U.S.. However, on the other hand, pressuring workers to work long hours would not sufficiently bridge the gap, nor would it be beneficial to technological improvement.

For the rest of our project, we used text analysis, and supervised and unsupervised techniques to dive deep into the problem.

II - Data Preprocessing

Load datasets

dt_issues = read.csv("data/issues_data.csv", header=TRUE)
dt_star = read.csv("data/stargazers.csv", header=TRUE)
dt_user = read.csv("data/users_data.csv", header=TRUE)

Saniety Check

# Inspect the dataset by taking the first 10 rows of each dataset. 
dt_issues %>% head(10)
dt_star %>% head(10)
dt_user %>% head(10)

Data Cleaning & Wrangling

# User dataset cleaning
# Cleaning
dt_user_cld <- dt_user %>% 
    # Join the issues dataset. 
    left_join(dt_issues, by="X_id") %>%
    select(bio, blog, company, created_at.x, followers, following, hireable, location, login, name, public_gists, 
           type, closed_at, updated_at.x, email, organizations_url, public_repos) %>%
    # Drop unused features. 
    # select(-X_id, -avatar_url, -events_url, -followers_url, -following_url, 
    #        -gists_url, -gravatar_id, -html_url, -node_id, -public_gists, -received_events_url, 
    #        -repos_url, -site_admin, -starred_url, -subscriptions_url, -type) %>%
    # Convert the time/date features to relative format. 
    mutate(created_at = lubridate::ymd_hms(created_at.x), 
           updated_at = lubridate::ymd_hms(updated_at.x)) %>%
    # Convert various factor type features to string type. 
    mutate(bio = as.character(bio), 
           blog = as.character(blog), 
           company = as.character(company), 
           email = as.character(email), 
           location = as.character(location), 
           login = as.character(login), 
           name = as.character(name), 
           organizations_url = as.character(organizations_url))
Column `X_id` joining factors with different levels, coercing to character vector
str(dt_user_cld)
'data.frame':   39987 obs. of  19 variables:
 $ bio              : chr  "" "" "" "" ...
 $ blog             : chr  "" "" "" "" ...
 $ company          : chr  "" "" "" "" ...
 $ created_at.x     : Factor w/ 39971 levels "2008-03-26T03:33:42Z",..: 23111 24348 13701 27767 34585 17517 19613 22254 23043 18383 ...
 $ followers        : int  9 4 7 2 0 2 13 5 1 0 ...
 $ following        : int  16 38 14 89 0 0 21 126 48 1 ...
 $ hireable         : Factor w/ 2 levels "","True": 1 1 1 1 1 1 1 1 1 1 ...
 $ location         : chr  "" "" "" "" ...
 $ login            : chr  "moloach" "bhxch" "YueNing" "BigFaceCatMhc" ...
 $ name             : chr  "" "Zhe Lee" "naodongbanana" "" ...
 $ public_gists     : int  0 0 0 0 0 0 1 24 0 0 ...
 $ type             : Factor w/ 1 level "User": 1 1 1 1 1 1 1 1 1 1 ...
 $ closed_at        : logi  NA NA NA NA NA NA ...
 $ updated_at.x     : Factor w/ 38705 levels "2015-10-22T09:47:56Z",..: 14522 14681 25825 31448 31450 37456 23803 31101 4862 19705 ...
 $ email            : chr  "" "mytempbh@outlook.com" "n1085633848@outlook.com" "" ...
 $ organizations_url: chr  "https://api.github.com/users/moloach/orgs" "https://api.github.com/users/bhxch/orgs" "https://api.github.com/users/YueNing/orgs" "https://api.github.com/users/BigFaceCatMhc/orgs" ...
 $ public_repos     : int  10 34 29 4 0 26 93 102 13 2 ...
 $ created_at       : POSIXct, format: "2016-07-12 05:17:50" "2016-08-27 14:04:23" "2015-06-19 13:57:11" ...
 $ updated_at       : POSIXct, format: "2019-03-09 00:15:05" "2019-03-09 09:30:45" "2019-03-25 10:50:50" ...
# Issues dataset cleaning
dt_issues_cld <- dt_issues %>%
    # Drop unused features. 
    select(created_at, body, comments, created_at, title, user.login) %>%
    # Convert the time/date features to relative format. 
    mutate(created_at = lubridate::ymd_hms(created_at), 
           # Convert the factor type features to the correct format. 
           body = as.character(body), 
           title = as.character(title), 
           user.login = as.character(user.login))

III - Data Analysis

1. Supporters’ Profile Analysis

1. What Companies/Universities are those programmers from?

company_info <- dt_user_cld %>% 
    group_by(company) %>%
    summarise(count = n()) %>%
    arrange(desc(count)) %>%
    filter(company != "")
# Display the top companies. 
company_info
# Define the company aggregation function. 
company_aggregation <- function(name) {
    # Make case insensitive. 
    orig_name <- name
    name <- toupper(name)
    # Detect pattern and change the company name accordingly. 
    if (grepl("百度|BAIDU|AIDU", name)) {
        target_name <- "Baidu"
    } else if (grepl("ENCENT|腾讯|TENCENT", name)) {
        target_name <- "Tencent"
    } else if (grepl("LIBABA|淘宝|AOBAO|LIPAY|阿里巴巴|LIYUN|阿里云", name)) {
        target_name <- "Alibaba"
    } else if (grepl("JD|京东", name)) {
        target_name <- "JD"
    } else if (grepl("ETEASE|网易", name)) {
        target_name <- "NetEase"
    } else if (grepl("EITUAN|美团", name)) {
        target_name <- "MeiTuan"
    } else if (grepl("YTEDANCE|字节|头条", name)) {
        target_name <- "ByteDance"
    } else if (grepl("ELEME|饿了", name)) {
        target_name <- "Eleme"
    } else if (grepl("UAWEI|华为", name)) {
        target_name <- "Huawei"
    } else if (grepl("DIDI|滴滴|嘀嘀", name)) {
        target_name <- "DiDi"
    } else {
        target_name <- orig_name
    }
    
    return (target_name)
}
# Define the education aggregation function. 
education_aggregation <- function(name) {
    # Make case insensitive
    orig_name <- name
    name <- toupper(name)
    # Detect pattern and change the education accordingly. 
    if (grepl("HEJIANG|ZJU|浙江大学|浙大", name)) {
        target_name <- "Zhejiang University"
    } else if (grepl("SINGHUA|清华", name)) {
        target_name <- "Tsinghua University"
    } else if (grepl("SHANGHAI JIAO TONG|SJTU|上海交大|上海交通", name)) {
        target_name <- "Shanghai Jiao Tong University"
    } else if (grepl("UESTC|电子科大|电子科技", name)) {
        target_name <- "University of Electronic Science and Technology of China"
    } else if (grepl("USTC|中科大|中国科学技术", name)) {
        target_name <- "University of Science and Technology of China"
    } else if (grepl("FUDAN|复旦", name)) {
        target_name <- "Fudan University"
    } else if (grepl("ARBIN|哈", name)) {
        target_name <- "Harbin Institute of Technology"
    } else if (grepl("BUPT|北邮|北京邮电", name)) {
        target_name <- "Beijing University of Post and Telecommunications"
    } else {
        target_name <- NA
    }
    
    return (target_name)
}
# Aggregating disparse companies. 
agg_companies <- rep(NA, nrow(company_info))
agg_education <- rep(NA, nrow(company_info))
for (i in 1:nrow(company_info)) {
    agg_companies[i] <- company_aggregation(company_info$company[i])
    agg_education[i] <- education_aggregation(company_info$company[i])
}
company_info_agg <- cbind(company_info, agg_companies, agg_education)
# Show the top ten companies which have the most number of developer support 996.icu
company_info_agg %>% group_by(agg_companies) %>%
    summarise(count = n()) %>%
    arrange(desc(count)) %>% 
    head(10)
# Show what universities are those developers from. 
company_info_agg %>% group_by(agg_education) %>%
    summarise(count = n()) %>%
    arrange(desc(count)) %>%
    filter(!is.na(agg_education)) %>%
    head(10)
Factor `agg_education` contains implicit NA, consider using `forcats::fct_explicit_na`
NA

2. What cities are those developers from?

# 
# Define the function for aggregating the cities. 
city_aggregation <- function(name) {
    # Make case insensitive. 
    orig_name <- name
    name <- toupper(name)
    # Detect pattern and change the education accordingly. 
    if (grepl("EIJING|北京", name)) {
        target_name <- "Beijing"
    } else if (grepl("HANGHAI|上海", name)) {
        target_name <- "Shanghai"
    } else if (grepl("ANGZHOU|杭州", name)) {
        target_name <- "Hangzhou"
    } else if (grepl("UANGZHOU|广州", name)) {
        target_name <- "Hangzhou"
    } else if (grepl("HENGDU|成都", name)) {
        target_name <- "Chengdu"
    } else if (grepl("ANJING|南京", name)) {
        target_name <- "Nanjing"
    } else if (grepl("INGAPORE|新加坡", name)) {
        target_name <- "Singapore"
    } else if (grepl("HONG KONG|香港|HK", name)) {
        target_name <- "Hong Kong"
    } else if (grepl("UHAN|武汉", name)) {
        target_name <- "Wuhan"
    } else {
        target_name <- orig_name
    }
    
    return (target_name)
}
city_info <- dt_user_cld %>% 
    group_by(location) %>%
    summarise(count = n()) %>%
    filter(location != "", 
           location != "China") %>%
    arrange(desc(count))
agg_cities <- rep(NA, nrow(city_info))
for (i in 1:nrow(city_info)) {
    agg_cities[i] <- city_aggregation(city_info$location[i])
} 
city_info_agg <- cbind(city_info, agg_cities)
# Showing the top ten cities that have the most developer support 996.icu
city_info_agg %>% group_by(agg_cities) %>%
    summarise(count = n()) %>%
    filter(agg_cities != "", 
           agg_cities != "China") %>%
    arrange(desc(count)) %>%
    head(10)

4. Distribution Plot of Supporters’ Information.

# Distribution graph of supporter's followers under 50. 
dist_ggplot <- dt_user_cld %>% filter(followers <= 50, following <= 50, public_repos <= 50) %>%
    ggplot() +
    geom_bar(aes(x = followers), col="black", fill="black", alpha=0.5) +
    geom_bar(aes(x = following), col="black", fill="red", alpha=0.5) +
    geom_bar(aes(x = public_repos), col="black", fill="blue", alpha=0.5)
    
dist_ggplot + 
    labs(x = "Followers (Black), Following (Red) and Public Repositories (Blue)", 
         y = "Count") +
    ggtitle("Distribution Plot")

5. Distribution Plot of Supporters’ Registration Duration.

# Calculate supporters' number of days since registered the github account. 
today <- lubridate::ymd("2019-04-29")
dt_user_cld <- dt_user_cld %>%
    # Calculate the duration and convert it to numerical value. 
    mutate(duration = as.numeric(as.duration(interval(created_at, today)), "days"))
# Showing average registration years. 
print(mean(dt_user_cld$duration)/365)
[1] 3.321946
# Distribution plot of registration days. 
dt_user_cld %>% ggplot(aes(x = duration/365)) + 
    geom_histogram(col="black", fill="grey", alpha = 0.7) +
    geom_vline(xintercept = mean(dt_user_cld$duration)/365, linetype = "dotted", color = "red", size = 1.5) +
    labs(y = "Frequency / Count", 
         x = "Number of Years Since Registration") +
    ggtitle("Distribution Plot of Supporters' Registration Duration")

2. Statistical Modeling

1. Analyzing the Relationship Between Followers and other factors.

# Select variables for analysis. 
user_stat <- dt_user_cld %>% 
    select(followers, following, public_repos, duration)
# Saniety Check
user_stat %>% head(10)
# Unsupervised Learning: PCA
user_pca <- prcomp(user_stat, center=TRUE, scale.=TRUE)
print(user_pca)
Standard deviations (1, .., p=4):
[1] 1.1689728 0.9810532 0.9575395 0.8684211

Rotation (n x k) = (4 x 4):
                   PC1        PC2         PC3        PC4
followers    0.3419542 -0.8727076  0.30951456  0.1601545
following    0.5162926  0.3258117  0.60978132 -0.5054259
public_repos 0.6008709  0.3352248 -0.09115495  0.7199092
duration     0.5054339 -0.1408987 -0.72391868 -0.4479128
summary(user_pca)
Importance of components:
                          PC1    PC2    PC3    PC4
Standard deviation     1.1690 0.9811 0.9575 0.8684
Proportion of Variance 0.3416 0.2406 0.2292 0.1885
Cumulative Proportion  0.3416 0.5822 0.8115 1.0000
# Supervised Learning: regression
# y = dt_user_cld$followers
# x = dt_user_cld$following, public_repos, duration
lm <- lm(followers ~ following+public_repos+duration, data = dt_user_cld)
summary(lm)

Call:
lm(formula = followers ~ following + public_repos + duration, 
    data = dt_user_cld)

Residuals:
    Min      1Q  Median      3Q     Max 
-1102.5   -12.8    -5.7     1.1 13756.7 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -1.006e+01  1.276e+00  -7.884 3.24e-15 ***
following     5.212e-02  3.965e-03  13.144  < 2e-16 ***
public_repos  6.555e-02  1.088e-02   6.023 1.73e-09 ***
duration      1.560e-02  9.397e-04  16.605  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 122.6 on 39983 degrees of freedom
Multiple R-squared:  0.01515,   Adjusted R-squared:  0.01508 
F-statistic: 205.1 on 3 and 39983 DF,  p-value: < 2.2e-16
# Because adjusted R-squared is very low, at 0.01508. We decided to further check linear assumptions by plotting it.
plot(lm)

# From the plots, we can tell that this data violated various linear assumption. Therefore, linear regression wouldn't work with this dataset.

3. Main Questions/Issues of Supporters

2. Text Analysis About Topics of 996 Movement.

# Setting the word-split engine
splitter <- worker(stop_word = "data/stopwords.txt")
# Splitting the words. 
seg <- c(splitter[dt_issues_cld$title], splitter[dt_issues_cld$body])
seg <- seg[nchar(seg) > 1]
# Encode the chinese word vector as UTF-8 format. 
Encoding(seg) <- "UTF-8"
# Extract the top 100 
seg_df <- data.frame(seg = seg) %>%
    group_by(seg) %>%
    summarise(freq = n()) %>%
    arrange(desc(freq)) %>%
    head(100)
# Generating word cloud (Chinese Version). 
font_family <- par("family")
par(family = "Adobe Heiti Std R")
wordcloud(words=seg_df$seg, freq=seg_df$freq, 
          colors=brewer.pal(8,"Dark2"), 
          scale=c(4, 0.8))

# Loading translated dataset. 
trans <- read.table("data/translate", sep="\t")[-1,]
seg_df <- cbind(seg_df, engl = trans$V2)
# Generating word cloud (English Version)
wordcloud(words=seg_df$engl, freq=seg_df$freq, 
          colors=brewer.pal(8,"Dark2"), 
          scale=c(4, 0.8))

IV - reference

Kenton, Will. “Social Good.” Investopedia, Investopedia, 12 Mar. 2019, www.investopedia.com/terms/s/social_good.asp.

Wei, Shiyang. “A pilot study on the Chinese internet environment.” International Conference on Advances in Education and Management. Springer, Berlin, Heidelberg, 2011.

Yuan, Li. “Learning China’s Forbidden History, So They Can Censor It.” The New York Times, The New York Times, 2 Jan. 2019, www.nytimes.com/2019/01/02/business/china-internet-censor.html.

“Chinese Developers Use Github to Protest against Country’s 996 Work Schedule.” South China Morning Post, 29 Mar. 2019, www.scmp.com/tech/start-ups/article/3003691/developers-lives-matter-chinese-software-engineers-use-github.

LS0tCnRpdGxlOiAiOTk2IEFuYWx5c2lzIgphdXRob3I6ICJaaGVuZyBaaGFuZywgIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KIyBhbHdheXMgY2xlYW4gdXAgUiBlbnZpcm9ubWVudApybShsaXN0ID0gbHMoKSkKCiMgbG9hZCBhbGwgcGFja2FnZXMgaGVyZQojIEJhc2ljIERhdGEgQW5hbHlzaXMgJiBXcmFuZ2xpbmcKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobHVicmlkYXRlKQoKIyBMaWJyYXJ5IGZvciBzcGxpdHRpbmcgdGhlIGNoaW5lc2Ugd29yZHMuIApsaWJyYXJ5KGppZWJhUkQpCmxpYnJhcnkoamllYmFSKQoKIyBMaWJyYXJ5IGZvciBnZW5lcmF0aW5nIHdvcmQgY2xvdWQuIApsaWJyYXJ5KHdvcmRjbG91ZCkKCiMgTGlicmFyeSBmb3IgdmlzdWFsaXppbmcgdGhlIDNkIHBsb3QuIApsaWJyYXJ5KHBsb3RseSkKYGBgCgoKIyBJIC0gSW50cm9kdWN0aW9uCiMjIyBCYXNlZCBvbiBkZWZpbml0aW9uIGZyb20gSW52ZXN0b3BlZGlhLmNvbSwgU29jaWFsIGdvb2QgbWVhbnMgInNvbWV0aGluZyB0aGF0IGJlbmVmaXRzIHRoZSBsYXJnZXN0IG51bWJlciBvZiBwZW9wbGUgaW4gdGhlIGxhcmdlc3QgcG9zc2libGUgd2F5LCBzdWNoIGFzIGNsZWFuIGFpciwgY2xlYW4gd2F0ZXIsIGhlYWx0aGNhcmUgYW5kIGxpdGVyYWN5LiIgU29jaWFsIGdvb2QgaXMgYWxzbyByZWZlcnJlZCB0byBhcyB0aGUgImNvbW1vbiBnb29kLiIgSW4gb3VyIHRvcGljLCB3ZSBob3BlIHRvIGRpc2N1c3MgYSBoZWF0ZWQgaXNzdWUgd2l0aGluIHRoZSBjb21tdW5pdHkgb2YgZGV2ZWxvcGVycy4gQmVmb3JlIHdlIGdvIGluIGRlcHRoIG9mIHRoZSBpc3N1ZSwgaGVyZSBhcmUgYSBjb3VwbGUgdGhpbmdzIGluZGljYXRpbmcgd2h5IGl0J3MgYW4gaW1wb3J0YW50IGlzc3VlOgoKIyMjIE92ZXJhbGwgQ2hpbmVzZSBJbnRlcm5ldCBlbnZpcm9ubWVudDoKCiogQSBjb3VwbGUgY29tcGFuaWVzIGhhdmUgdHJpZWQgdG8gZW50ZXIgdGhlIGx1Y3JhdGl2ZSBtYXJrZXQgb2YgbWFpbmxhbmQgQ2hpbmEsIGhvd2V2ZXIsIG1hbnkgb2YgdGhlbSBmYWNlZCBvYnN0YWNsZXMuIEEgZmFtb3VzIGV4YW1wbGUgaXMgZnJvbSBHb29nbGUsIHdobyBzcGVjaWZpY2FsbHkgZGVzaWduZWQgdGhlIGRyYWdvbmZseSBwbGFuIGZvciBDaGluZXNlIE1hcmtldCwgdGhhdCBmYWlsZWQgZHVlIHRvIHJlZ3VsYXRpb24gYXMgd2VsbCBhcyBsYWNrIG9mIGludGVyZXN0IGZyb20gdGhlIGdlbmVyYWwgcHVibGljLiBlQmF5IHdhcyBvbmUgb2YgdGhlIGVhcmxpZXN0IGNvdW50ZXJwYXJ0cyB0aGF0IGxhaWQgZXllcyBvbiBBc2lhIG1hcmtldCwgYW5kIGFsbCBvZiB0aGVzZSBiaWcgY29tcGFuaWVzIGZhaWxlZCBmb3IgdGhlIHNhbWUgcmVhc29uOiB0aGV5IGFsbCBoYWQgdW5zdWNjZXNzZnVsIGludGVncmF0aW9uIG9mIGluY29tcGF0aWJsZSBjdWx0dXJlcy4gV2UgcGVyc29uYWxseSBjYWxsIGl0ICJhdHRlbXB0IG9mIGN1bHR1cmFsIGltcGVyaWFsaXNtIHdpdGhpbiB0aGUgZmllbGQgb2YgaW50ZXJuZXQuIiBUaGUgYmlnIGNvbXBhbmllcyB0b29rIG92ZXIgbWFueSBwbGFjZXMgd2l0aCBzdWNjZXNzIHdpdGhvdXQgY2hhbmdpbmcgbXVjaCBvZiBpdHMgYnVzaW5lc3MgbW9kZWwsIG9yIGFwcHJvYWNoIG9mIHRoZSBsb2NhbCBhdWRpZW5jZS4gSG93ZXZlciwgdGhlcmUgaXMgYSB0cmVtZW5kb3VzIGN1bHR1cmUgZGlmZmVyZW5jZSBiZXR3ZWVuIHdlc3Rlcm4gYW5kIGVhc3Rlcm4gc29jaWV0eS4gT25lIGV4YW1wbGUgaXMgdGhlIHlvdW5nIGdlbmVyYXRlIGluIHRoZSBVbml0ZWQgU3RhdGVzIHVzZSBGYWNlYm9vaywgVHdpdHRlciwgSW5zdGFncmFtLCBTbmFwY2hhdCBhbmQgb3RoZXIgc29jaWFsIHBsYXRmb3JtcyBzaW11bHRhbmVvdXNseS4gVGVlbnMgaW4gQ2hpbmEgYWxsIG9ubHkgdXNlIG9uZSBzb2NpYWwgbWVkaWEgYXBwLCBXZWNoYXQsIHRoYXQgY291bGQgYWxzbyBwYXkgZm9yIGJpbGxzLCBjYWxsIGFuIHViZXIsIGJvb2sgYSBtb3ZpZSwgZmluZCB0aGUgcmVzdGF1cmFudC4gQ29uc3VtZXIgYmVoYXZpb3IgYW5kIGN1bHR1cmUgZGlmZmVyZW5jZSBpcyB3aGF0IHB1bGxlZCB0aGVzZSBiaWcgY29tcGFuaWVzIGJhY2suIAoKKiBDaGluZXNlIGdvdmVybm1lbnQgaXMga25vd24gZm9yIGl0cyByZWd1bGF0aW9uIGFuZCBjZW5zb3JzaGlwLiBBY2NvcmRpbmcgdG8gYW4gYXJ0aWNsZSBwdWJsaXNoZWQgYnkgTmV3IFlvcmsgVGltZXMsIHRoZSBuZXcgcHJlc2lkZW50IGhvcGVzIHRvIHVzZSB0aGUgSW50ZXJuZXQgdG8gc3RyZW5ndGhlbiBDb21tdW5pc3QgUGFydHkncyByb2xlIG9uIHRoZSBzb2NpZXR5LiBNYWpvcml0eSBvZiB0aGUgeW91bmcgZ2VuZXJhdGlvbiBpcyBpbmRpZmZlcmVudCB0byBwb2xpdGljcywgYWx0aG91Z2ggbWFueSBhcmUgdmljdGltcyBvZiBjZW5zb3JzaGlwIGFzIHdlbGwgYXMgY2Vuc29yc2hpcCBmYWN0b3J5IHdvcmtlcnMuIAoKIyMjIEZyb20gYSBicmllZiBpbnRyb2R1Y3Rpb24gb2YgaG93IHRoZSBvdmVyYWxsIENoaW5lc2UgSW50ZXJuZXQgZW52aXJvbm1lbnQgaXMgZGlmZmVyZW50IGZyb20gdGhlIFVuaXRlZCBTdGF0ZXMsIGFuZCBvdGhlciBkZXZlbG9wZWQgY291bnRyaWVzLCB3ZSB3aWxsIG5vdyBjb25uZWN0IHRvIHRoZSB0b3BpYyBvZiBpbnRlcmVzdCB0b2RheTogOTk2LklDVSBldmVudC4gCgojIyMgIDk5Ni5JQ1UgaXMgYSByZWZlcmVuY2UgdG8gdGhlIGdydWVsaW5nIGFuZCBpbGxlZ2FsIHdvcmtpbmcgaG91cnMgb2YgbWFueSB0ZWNoIGNvbXBhbmllcyBpbiBDaGluYSAtIGZyb20gOWFtIHRvIDlwbSwgNiBkYXlzIGEgd2Vlay4gVGhlIG5hbWUg4oCcOTk2LklDVeKAnSBjYW1lIGZyb20gdGhlIGRlc2NyaXB0aW9uIGluIHRoZSByZXBvc2l0b3J5LCDigJxCeSBmb2xsb3dpbmcgdGhlICc5OTYnIHdvcmsgc2NoZWR1bGUsIHlvdSBhcmUgcmlza2luZyB5b3Vyc2VsZiBnZXR0aW5nIGludG8gdGhlIElDVSAoSW50ZW5zaXZlIENhcmUgVW5pdCku4oCdIFRoZSBldmVudCBjYW1lIHRvIHRoZSBwZWFrIHdoZW4gSmFjayBNYSwgdGhlIGZvdW5kZXIgb2YgdGhlIGUtY29tbWVyY2UgZ2lhbnQgQWxpYmFiYSBHcm91cCwgZ2F2ZSB0aGUgZm9sbG93aW5nIHJlbWFya3MgaW4gbWlkLUFwcmlsIDIwMTk6IOKAnEl0IGlzIGEgaHVnZSBibGVzc2luZyB0aGF0IHdlIGNhbiB3b3JrIDk5Ni7igJ0gQWxpYmFiYSBvd25zIHRoZSBBbWF6b24gb2YgQ2hpbmEsIGFzIHdlbGwgYXMgdGhlIGJpZ2dlc3QgY2xvdWQgY29tcHV0aW5nIHBsYXRmb3JtIGluIG1haW5sYW5kLiBIZSBzYWlkLCDigJxJZiB5b3UgZG8gbm90IGRvIDk5NiB3aGVuIHlvdSBhcmUgeW91bmcsIHdoZW4gd2lsbCB5b3UgZG8gaXQuIElmIHlvdSBkb27igJl0IHB1dCBtb3JlIHRpbWUgYW5kIGVuZXJneSB0aGFuIG90aGVycywgaG93IGNhbiB5b3UgYWNoaWV2ZSB0aGUgc3VjY2VzcyB5b3Ugd2FudD/igJ0gU3VjaCByZW1hcmsgaGFzIHJlY2VpdmVkIGNvbnRyb3ZlcnNpYWwgY29tbWVudHMgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb3VudHJ5LiBDdXJyZW50bHksIDk5Ni5JQ1UgcmVwb3NpdG9yeSBpcyByYW5rZWQgTm8uMiBvbiB0aGUgVHJlbmRpbmcgcGFnZSBmb3IgZ2l0aHViLCB3b3JsZCdzIGxhcmdlc3QgZGV2ZWxvcGVyIGNvbW11bml0eSwgcmlnaHQgYWZ0ZXIgdGhlIHJlcG9zaXRvcnkgdGhhdCBob3N0cyBhbGwgQWxnb3JpdGhtcyBpbXBsZW1lbnRlZCBpbiBQeXRob24uIE1pY3Jvc29mdCBhbmQgR2l0SHViIFdvcmtlcnMgc3RhcnRlZCB0aGVpciBvd24gcmVwb3NpdG9yeSB0byBzdXBwb3J0IDk5Ni5JQ1UgbW92ZW1lbnQuIAojIyMgVGhpcyBtb3ZlbWVudCBpcyBwZXJzb25hbGx5IHNpZ25pZmljYW50IHRvIG91ciB0ZWFtLCBiZWNhdXNlIGJvdGggb2YgdXMgaGF2ZSBpbnRlcmFjdGVkIHdpdGggdGhlIGNvbXBhbmllcyBtZW50aW9uZWQgYWJvdmUgYW5kIGhhdmUgZnJpZW5kcyBhbmQgZmFtaWxpZXMgd2hvIHdvcmsgaW4gVGVjaCBpbiBDaGluYS4gV2UgaGF2ZSB3aXRuZXNzZWQgdGhlIGNvbnNlcXVlbmNlcyBjYXVzZWQgbG9uZyBob3VycywgYW5kIHVucHJvZHVjdGl2ZSB3b3JrIGluIHRoZSBUZWNoIGluZHVzdHJ5IGluIENoaW5hLiBPbiBvbmUgaGFuZCwgdGhlIENoaW5lc2Ugb3ZlcmFsbCBJbnRlcm5ldCBlbnZpcm9ubWVudCBpcyBkaWZmZXJlbnQgZnJvbSB0aGUgVW5pdGVkIFN0YXRlcywgYXMgd2VsbCBhcyBpdCBpcyBhdCBsZWFzdCAxMCB5ZWFycyBiZWhpbmQgdGhlIFUuUy4uIEhvd2V2ZXIsIG9uIHRoZSBvdGhlciBoYW5kLCBwcmVzc3VyaW5nIHdvcmtlcnMgdG8gd29yayBsb25nIGhvdXJzIHdvdWxkIG5vdCBzdWZmaWNpZW50bHkgYnJpZGdlIHRoZSBnYXAsIG5vciB3b3VsZCBpdCBiZSBiZW5lZmljaWFsIHRvIHRlY2hub2xvZ2ljYWwgaW1wcm92ZW1lbnQuCgojIyMgRm9yIHRoZSByZXN0IG9mIG91ciBwcm9qZWN0LCB3ZSB1c2VkIHRleHQgYW5hbHlzaXMsIGFuZCBzdXBlcnZpc2VkIGFuZCB1bnN1cGVydmlzZWQgdGVjaG5pcXVlcyB0byBkaXZlIGRlZXAgaW50byB0aGUgcHJvYmxlbS4KCgojIElJIC0gRGF0YSBQcmVwcm9jZXNzaW5nCgojIyMgTG9hZCBkYXRhc2V0cwoKYGBge3J9CmR0X2lzc3VlcyA9IHJlYWQuY3N2KCJkYXRhL2lzc3Vlc19kYXRhLmNzdiIsIGhlYWRlcj1UUlVFKQpkdF9zdGFyID0gcmVhZC5jc3YoImRhdGEvc3RhcmdhemVycy5jc3YiLCBoZWFkZXI9VFJVRSkKZHRfdXNlciA9IHJlYWQuY3N2KCJkYXRhL3VzZXJzX2RhdGEuY3N2IiwgaGVhZGVyPVRSVUUpCmBgYAoKIyMjIFNhbmlldHkgQ2hlY2sKCmBgYHtyfQojIEluc3BlY3QgdGhlIGRhdGFzZXQgYnkgdGFraW5nIHRoZSBmaXJzdCAxMCByb3dzIG9mIGVhY2ggZGF0YXNldC4gCmR0X2lzc3VlcyAlPiUgaGVhZCgxMCkKZHRfc3RhciAlPiUgaGVhZCgxMCkKZHRfdXNlciAlPiUgaGVhZCgxMCkKYGBgCgojIyMgRGF0YSBDbGVhbmluZyAmIFdyYW5nbGluZwoKYGBge3J9CiMgVXNlciBkYXRhc2V0IGNsZWFuaW5nCiMgQ2xlYW5pbmcKZHRfdXNlcl9jbGQgPC0gZHRfdXNlciAlPiUgCiAgICAjIEpvaW4gdGhlIGlzc3VlcyBkYXRhc2V0LiAKICAgIGxlZnRfam9pbihkdF9pc3N1ZXMsIGJ5PSJYX2lkIikgJT4lCiAgICBzZWxlY3QoYmlvLCBibG9nLCBjb21wYW55LCBjcmVhdGVkX2F0LngsIGZvbGxvd2VycywgZm9sbG93aW5nLCBoaXJlYWJsZSwgbG9jYXRpb24sIGxvZ2luLCBuYW1lLCBwdWJsaWNfZ2lzdHMsIAogICAgICAgICAgIHR5cGUsIGNsb3NlZF9hdCwgdXBkYXRlZF9hdC54LCBlbWFpbCwgb3JnYW5pemF0aW9uc191cmwsIHB1YmxpY19yZXBvcykgJT4lCiAgICAjIERyb3AgdW51c2VkIGZlYXR1cmVzLiAKICAgICMgc2VsZWN0KC1YX2lkLCAtYXZhdGFyX3VybCwgLWV2ZW50c191cmwsIC1mb2xsb3dlcnNfdXJsLCAtZm9sbG93aW5nX3VybCwgCiAgICAjICAgICAgICAtZ2lzdHNfdXJsLCAtZ3JhdmF0YXJfaWQsIC1odG1sX3VybCwgLW5vZGVfaWQsIC1wdWJsaWNfZ2lzdHMsIC1yZWNlaXZlZF9ldmVudHNfdXJsLCAKICAgICMgICAgICAgIC1yZXBvc191cmwsIC1zaXRlX2FkbWluLCAtc3RhcnJlZF91cmwsIC1zdWJzY3JpcHRpb25zX3VybCwgLXR5cGUpICU+JQogICAgIyBDb252ZXJ0IHRoZSB0aW1lL2RhdGUgZmVhdHVyZXMgdG8gcmVsYXRpdmUgZm9ybWF0LiAKICAgIG11dGF0ZShjcmVhdGVkX2F0ID0gbHVicmlkYXRlOjp5bWRfaG1zKGNyZWF0ZWRfYXQueCksIAogICAgICAgICAgIHVwZGF0ZWRfYXQgPSBsdWJyaWRhdGU6OnltZF9obXModXBkYXRlZF9hdC54KSkgJT4lCiAgICAjIENvbnZlcnQgdmFyaW91cyBmYWN0b3IgdHlwZSBmZWF0dXJlcyB0byBzdHJpbmcgdHlwZS4gCiAgICBtdXRhdGUoYmlvID0gYXMuY2hhcmFjdGVyKGJpbyksIAogICAgICAgICAgIGJsb2cgPSBhcy5jaGFyYWN0ZXIoYmxvZyksIAogICAgICAgICAgIGNvbXBhbnkgPSBhcy5jaGFyYWN0ZXIoY29tcGFueSksIAogICAgICAgICAgIGVtYWlsID0gYXMuY2hhcmFjdGVyKGVtYWlsKSwgCiAgICAgICAgICAgbG9jYXRpb24gPSBhcy5jaGFyYWN0ZXIobG9jYXRpb24pLCAKICAgICAgICAgICBsb2dpbiA9IGFzLmNoYXJhY3Rlcihsb2dpbiksIAogICAgICAgICAgIG5hbWUgPSBhcy5jaGFyYWN0ZXIobmFtZSksIAogICAgICAgICAgIG9yZ2FuaXphdGlvbnNfdXJsID0gYXMuY2hhcmFjdGVyKG9yZ2FuaXphdGlvbnNfdXJsKSkKCnN0cihkdF91c2VyX2NsZCkKCiMgSXNzdWVzIGRhdGFzZXQgY2xlYW5pbmcKZHRfaXNzdWVzX2NsZCA8LSBkdF9pc3N1ZXMgJT4lCiAgICAjIERyb3AgdW51c2VkIGZlYXR1cmVzLiAKICAgIHNlbGVjdChjcmVhdGVkX2F0LCBib2R5LCBjb21tZW50cywgY3JlYXRlZF9hdCwgdGl0bGUsIHVzZXIubG9naW4pICU+JQogICAgIyBDb252ZXJ0IHRoZSB0aW1lL2RhdGUgZmVhdHVyZXMgdG8gcmVsYXRpdmUgZm9ybWF0LiAKICAgIG11dGF0ZShjcmVhdGVkX2F0ID0gbHVicmlkYXRlOjp5bWRfaG1zKGNyZWF0ZWRfYXQpLCAKICAgICAgICAgICAjIENvbnZlcnQgdGhlIGZhY3RvciB0eXBlIGZlYXR1cmVzIHRvIHRoZSBjb3JyZWN0IGZvcm1hdC4gCiAgICAgICAgICAgYm9keSA9IGFzLmNoYXJhY3Rlcihib2R5KSwgCiAgICAgICAgICAgdGl0bGUgPSBhcy5jaGFyYWN0ZXIodGl0bGUpLCAKICAgICAgICAgICB1c2VyLmxvZ2luID0gYXMuY2hhcmFjdGVyKHVzZXIubG9naW4pKQpgYGAKCiMgSUlJIC0gRGF0YSBBbmFseXNpcwoKIyMgMS4gU3VwcG9ydGVycycgUHJvZmlsZSBBbmFseXNpcwoKIyMjIDEuIFdoYXQgQ29tcGFuaWVzL1VuaXZlcnNpdGllcyBhcmUgdGhvc2UgcHJvZ3JhbW1lcnMgZnJvbT8KCmBgYHtyfQpjb21wYW55X2luZm8gPC0gZHRfdXNlcl9jbGQgJT4lIAogICAgZ3JvdXBfYnkoY29tcGFueSkgJT4lCiAgICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogICAgYXJyYW5nZShkZXNjKGNvdW50KSkgJT4lCiAgICBmaWx0ZXIoY29tcGFueSAhPSAiIikKCiMgRGlzcGxheSB0aGUgdG9wIGNvbXBhbmllcy4gCmNvbXBhbnlfaW5mbwpgYGAKCgpgYGB7cn0KIyBEZWZpbmUgdGhlIGNvbXBhbnkgYWdncmVnYXRpb24gZnVuY3Rpb24uIApjb21wYW55X2FnZ3JlZ2F0aW9uIDwtIGZ1bmN0aW9uKG5hbWUpIHsKICAgICMgTWFrZSBjYXNlIGluc2Vuc2l0aXZlLiAKICAgIG9yaWdfbmFtZSA8LSBuYW1lCiAgICBuYW1lIDwtIHRvdXBwZXIobmFtZSkKICAgICMgRGV0ZWN0IHBhdHRlcm4gYW5kIGNoYW5nZSB0aGUgY29tcGFueSBuYW1lIGFjY29yZGluZ2x5LiAKICAgIGlmIChncmVwbCgi55m+5bqmfEJBSURVfEFJRFUiLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJCYWlkdSIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIkVOQ0VOVHzohb7orq98VEVOQ0VOVCIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIlRlbmNlbnQiCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJMSUJBQkF85reY5a6dfEFPQkFPfExJUEFZfOmYv+mHjOW3tOW3tHxMSVlVTnzpmL/ph4zkupEiLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJBbGliYWJhIgogICAgfSBlbHNlIGlmIChncmVwbCgiSkR85Lqs5LicIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiSkQiCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJFVEVBU0V8572R5piTIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiTmV0RWFzZSIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIkVJVFVBTnznvo7lm6IiLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJNZWlUdWFuIgogICAgfSBlbHNlIGlmIChncmVwbCgiWVRFREFOQ0V85a2X6IqCfOWktOadoSIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIkJ5dGVEYW5jZSIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIkVMRU1FfOmlv+S6hiIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIkVsZW1lIgogICAgfSBlbHNlIGlmIChncmVwbCgiVUFXRUl85Y2O5Li6IiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiSHVhd2VpIgogICAgfSBlbHNlIGlmIChncmVwbCgiRElESXzmu7Tmu7R85ZiA5ZiAIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiRGlEaSIKICAgIH0gZWxzZSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gb3JpZ19uYW1lCiAgICB9CiAgICAKICAgIHJldHVybiAodGFyZ2V0X25hbWUpCn0KCiMgRGVmaW5lIHRoZSBlZHVjYXRpb24gYWdncmVnYXRpb24gZnVuY3Rpb24uIAplZHVjYXRpb25fYWdncmVnYXRpb24gPC0gZnVuY3Rpb24obmFtZSkgewogICAgIyBNYWtlIGNhc2UgaW5zZW5zaXRpdmUKICAgIG9yaWdfbmFtZSA8LSBuYW1lCiAgICBuYW1lIDwtIHRvdXBwZXIobmFtZSkKICAgICMgRGV0ZWN0IHBhdHRlcm4gYW5kIGNoYW5nZSB0aGUgZWR1Y2F0aW9uIGFjY29yZGluZ2x5LiAKICAgIGlmIChncmVwbCgiSEVKSUFOR3xaSlV85rWZ5rGf5aSn5a2mfOa1meWkpyIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIlpoZWppYW5nIFVuaXZlcnNpdHkiCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJTSU5HSFVBfOa4heWNjiIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIlRzaW5naHVhIFVuaXZlcnNpdHkiCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJTSEFOR0hBSSBKSUFPIFRPTkd8U0pUVXzkuIrmtbfkuqTlpKd85LiK5rW35Lqk6YCaIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiU2hhbmdoYWkgSmlhbyBUb25nIFVuaXZlcnNpdHkiCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJVRVNUQ3znlLXlrZDnp5HlpKd855S15a2Q56eR5oqAIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiVW5pdmVyc2l0eSBvZiBFbGVjdHJvbmljIFNjaWVuY2UgYW5kIFRlY2hub2xvZ3kgb2YgQ2hpbmEiCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJVU1RDfOS4reenkeWkp3zkuK3lm73np5HlrabmioDmnK8iLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJVbml2ZXJzaXR5IG9mIFNjaWVuY2UgYW5kIFRlY2hub2xvZ3kgb2YgQ2hpbmEiCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJGVURBTnzlpI3ml6YiLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJGdWRhbiBVbml2ZXJzaXR5IgogICAgfSBlbHNlIGlmIChncmVwbCgiQVJCSU585ZOIIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiSGFyYmluIEluc3RpdHV0ZSBvZiBUZWNobm9sb2d5IgogICAgfSBlbHNlIGlmIChncmVwbCgiQlVQVHzljJfpgq585YyX5Lqs6YKu55S1IiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiQmVpamluZyBVbml2ZXJzaXR5IG9mIFBvc3QgYW5kIFRlbGVjb21tdW5pY2F0aW9ucyIKICAgIH0gZWxzZSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gTkEKICAgIH0KICAgIAogICAgcmV0dXJuICh0YXJnZXRfbmFtZSkKfQoKIyBBZ2dyZWdhdGluZyBkaXNwYXJzZSBjb21wYW5pZXMuIAphZ2dfY29tcGFuaWVzIDwtIHJlcChOQSwgbnJvdyhjb21wYW55X2luZm8pKQphZ2dfZWR1Y2F0aW9uIDwtIHJlcChOQSwgbnJvdyhjb21wYW55X2luZm8pKQpmb3IgKGkgaW4gMTpucm93KGNvbXBhbnlfaW5mbykpIHsKICAgIGFnZ19jb21wYW5pZXNbaV0gPC0gY29tcGFueV9hZ2dyZWdhdGlvbihjb21wYW55X2luZm8kY29tcGFueVtpXSkKICAgIGFnZ19lZHVjYXRpb25baV0gPC0gZWR1Y2F0aW9uX2FnZ3JlZ2F0aW9uKGNvbXBhbnlfaW5mbyRjb21wYW55W2ldKQp9CmNvbXBhbnlfaW5mb19hZ2cgPC0gY2JpbmQoY29tcGFueV9pbmZvLCBhZ2dfY29tcGFuaWVzLCBhZ2dfZWR1Y2F0aW9uKQpgYGAKCgpgYGB7cn0KIyBTaG93IHRoZSB0b3AgdGVuIGNvbXBhbmllcyB3aGljaCBoYXZlIHRoZSBtb3N0IG51bWJlciBvZiBkZXZlbG9wZXIgc3VwcG9ydCA5OTYuaWN1CmNvbXBhbnlfaW5mb19hZ2cgJT4lIGdyb3VwX2J5KGFnZ19jb21wYW5pZXMpICU+JQogICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUKICAgIGFycmFuZ2UoZGVzYyhjb3VudCkpICU+JSAKICAgIGhlYWQoMTApCgojIFNob3cgd2hhdCB1bml2ZXJzaXRpZXMgYXJlIHRob3NlIGRldmVsb3BlcnMgZnJvbS4gCmNvbXBhbnlfaW5mb19hZ2cgJT4lIGdyb3VwX2J5KGFnZ19lZHVjYXRpb24pICU+JQogICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUKICAgIGFycmFuZ2UoZGVzYyhjb3VudCkpICU+JQogICAgZmlsdGVyKCFpcy5uYShhZ2dfZWR1Y2F0aW9uKSkgJT4lCiAgICBoZWFkKDEwKQogICAgCmBgYAoKIyMjIDIuIFdoYXQgY2l0aWVzIGFyZSB0aG9zZSBkZXZlbG9wZXJzIGZyb20/IAoKCmBgYHtyfQojIAojIERlZmluZSB0aGUgZnVuY3Rpb24gZm9yIGFnZ3JlZ2F0aW5nIHRoZSBjaXRpZXMuIApjaXR5X2FnZ3JlZ2F0aW9uIDwtIGZ1bmN0aW9uKG5hbWUpIHsKICAgICMgTWFrZSBjYXNlIGluc2Vuc2l0aXZlLiAKICAgIG9yaWdfbmFtZSA8LSBuYW1lCiAgICBuYW1lIDwtIHRvdXBwZXIobmFtZSkKICAgICMgRGV0ZWN0IHBhdHRlcm4gYW5kIGNoYW5nZSB0aGUgZWR1Y2F0aW9uIGFjY29yZGluZ2x5LiAKICAgIGlmIChncmVwbCgiRUlKSU5HfOWMl+S6rCIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIkJlaWppbmciCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJIQU5HSEFJfOS4iua1tyIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIlNoYW5naGFpIgogICAgfSBlbHNlIGlmIChncmVwbCgiQU5HWkhPVXzmna3lt54iLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJIYW5nemhvdSIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIlVBTkdaSE9VfOW5v+W3niIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIkhhbmd6aG91IgogICAgfSBlbHNlIGlmIChncmVwbCgiSEVOR0RVfOaIkOmDvSIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIkNoZW5nZHUiCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJBTkpJTkd85Y2X5LqsIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiTmFuamluZyIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIklOR0FQT1JFfOaWsOWKoOWdoSIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIlNpbmdhcG9yZSIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIkhPTkcgS09OR3zpppnmuK98SEsiLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJIb25nIEtvbmciCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJVSEFOfOatpuaxiSIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIld1aGFuIgogICAgfSBlbHNlIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSBvcmlnX25hbWUKICAgIH0KICAgIAogICAgcmV0dXJuICh0YXJnZXRfbmFtZSkKfQoKY2l0eV9pbmZvIDwtIGR0X3VzZXJfY2xkICU+JSAKICAgIGdyb3VwX2J5KGxvY2F0aW9uKSAlPiUKICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lCiAgICBmaWx0ZXIobG9jYXRpb24gIT0gIiIsIAogICAgICAgICAgIGxvY2F0aW9uICE9ICJDaGluYSIpICU+JQogICAgYXJyYW5nZShkZXNjKGNvdW50KSkKCmFnZ19jaXRpZXMgPC0gcmVwKE5BLCBucm93KGNpdHlfaW5mbykpCmZvciAoaSBpbiAxOm5yb3coY2l0eV9pbmZvKSkgewogICAgYWdnX2NpdGllc1tpXSA8LSBjaXR5X2FnZ3JlZ2F0aW9uKGNpdHlfaW5mbyRsb2NhdGlvbltpXSkKfSAKCmNpdHlfaW5mb19hZ2cgPC0gY2JpbmQoY2l0eV9pbmZvLCBhZ2dfY2l0aWVzKQpgYGAKCmBgYHtyfQojIFNob3dpbmcgdGhlIHRvcCB0ZW4gY2l0aWVzIHRoYXQgaGF2ZSB0aGUgbW9zdCBkZXZlbG9wZXIgc3VwcG9ydCA5OTYuaWN1CmNpdHlfaW5mb19hZ2cgJT4lIGdyb3VwX2J5KGFnZ19jaXRpZXMpICU+JQogICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUKICAgIGZpbHRlcihhZ2dfY2l0aWVzICE9ICIiLCAKICAgICAgICAgICBhZ2dfY2l0aWVzICE9ICJDaGluYSIpICU+JQogICAgYXJyYW5nZShkZXNjKGNvdW50KSkgJT4lCiAgICBoZWFkKDEwKQpgYGAKCiMjIyAzLiBTdW1tYXJ5IFN0YXRpc3RpY3Mgb2YgU3VwcG9ydGVycycgUmVsYXRlZCBJbmZvcm1hdGlvbi4gCgpgYGB7cn0KIyBTdW1tYXJ5IHN0YXRpc3RpY3Mgb2Ygc3VwcG9ydGVycycgZ2l0aHViIGFjY291bnQuIApkdF91c2VyX2NsZCAlPiUgc2VsZWN0KGZvbGxvd2VycywgZm9sbG93aW5nLCBwdWJsaWNfcmVwb3MpICU+JQogICAgZ2F0aGVyKHN0YXRfdHlwZSwgbnVtYmVyLCBmb2xsb3dlcnMsIGZvbGxvd2luZywgcHVibGljX3JlcG9zKSAlPiUKIyAgICBmaWx0ZXIobnVtYmVyIDw9IDEwMDAwKSAlPiUKICAgIGdncGxvdChhZXMoeCA9IGZhY3RvcihzdGF0X3R5cGUpLCB5ID0gbG9nKG51bWJlcikpKSArCiAgICBnZW9tX2ppdHRlcihjb2xvciA9ICJncmV5Iiwgd2lkdGggPSAuMikgKwogICAgZ2VvbV9ib3hwbG90KGFscGhhPTAuNikgKwogICAgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gIm1lYW4iLCBnZW9tID0gInBvaW50Iiwgc2l6ZSA9IDUsIGNvbG9yID0gInJlZCIsIHNoYXBlID0gMTUpICsKICAgIGxhYnMoeCA9ICJTdW1tYXJ5IFN0YXRpc3RpY3Mgb2YgRm9sbG93ZXJzLCBGb2xsb3dpbmcgYW5kIFB1YmxpYyBSZXBvcywgTWVhbiAocmVkKSIsIAogICAgICAgICB5ID0gIlJlbGF0aXZlIFZhbHVlcyIpICsKICAgIGdndGl0bGUoIlN1bW1hcnkgU3RhdGlzdGljcyBQbG90IChMb2cgVHJhbnNmb3JtZWQpIikKICAgIAogICAgCmBgYAoKIyMjIDQuIERpc3RyaWJ1dGlvbiBQbG90IG9mIFN1cHBvcnRlcnMnIEluZm9ybWF0aW9uLiAKCmBgYHtyfQojIERpc3RyaWJ1dGlvbiBncmFwaCBvZiBzdXBwb3J0ZXIncyBmb2xsb3dlcnMgdW5kZXIgNTAuIApkaXN0X2dncGxvdCA8LSBkdF91c2VyX2NsZCAlPiUgZmlsdGVyKGZvbGxvd2VycyA8PSA1MCwgZm9sbG93aW5nIDw9IDUwLCBwdWJsaWNfcmVwb3MgPD0gNTApICU+JQogICAgZ2dwbG90KCkgKwogICAgZ2VvbV9iYXIoYWVzKHggPSBmb2xsb3dlcnMpLCBjb2w9ImJsYWNrIiwgZmlsbD0iYmxhY2siLCBhbHBoYT0wLjUpICsKICAgIGdlb21fYmFyKGFlcyh4ID0gZm9sbG93aW5nKSwgY29sPSJibGFjayIsIGZpbGw9InJlZCIsIGFscGhhPTAuNSkgKwogICAgZ2VvbV9iYXIoYWVzKHggPSBwdWJsaWNfcmVwb3MpLCBjb2w9ImJsYWNrIiwgZmlsbD0iYmx1ZSIsIGFscGhhPTAuNSkKICAgIApkaXN0X2dncGxvdCArIAogICAgbGFicyh4ID0gIkZvbGxvd2VycyAoQmxhY2spLCBGb2xsb3dpbmcgKFJlZCkgYW5kIFB1YmxpYyBSZXBvc2l0b3JpZXMgKEJsdWUpIiwgCiAgICAgICAgIHkgPSAiQ291bnQiKSArCiAgICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gUGxvdCIpCmBgYAoKIyMjIDUuIERpc3RyaWJ1dGlvbiBQbG90IG9mIFN1cHBvcnRlcnMnIFJlZ2lzdHJhdGlvbiBEdXJhdGlvbi4gCgpgYGB7cn0KIyBDYWxjdWxhdGUgc3VwcG9ydGVycycgbnVtYmVyIG9mIGRheXMgc2luY2UgcmVnaXN0ZXJlZCB0aGUgZ2l0aHViIGFjY291bnQuIAp0b2RheSA8LSBsdWJyaWRhdGU6OnltZCgiMjAxOS0wNC0yOSIpCmR0X3VzZXJfY2xkIDwtIGR0X3VzZXJfY2xkICU+JQogICAgIyBDYWxjdWxhdGUgdGhlIGR1cmF0aW9uIGFuZCBjb252ZXJ0IGl0IHRvIG51bWVyaWNhbCB2YWx1ZS4gCiAgICBtdXRhdGUoZHVyYXRpb24gPSBhcy5udW1lcmljKGFzLmR1cmF0aW9uKGludGVydmFsKGNyZWF0ZWRfYXQsIHRvZGF5KSksICJkYXlzIikpCgojIFNob3dpbmcgYXZlcmFnZSByZWdpc3RyYXRpb24geWVhcnMuIApwcmludChtZWFuKGR0X3VzZXJfY2xkJGR1cmF0aW9uKS8zNjUpCgojIERpc3RyaWJ1dGlvbiBwbG90IG9mIHJlZ2lzdHJhdGlvbiBkYXlzLiAKZHRfdXNlcl9jbGQgJT4lIGdncGxvdChhZXMoeCA9IGR1cmF0aW9uLzM2NSkpICsgCiAgICBnZW9tX2hpc3RvZ3JhbShjb2w9ImJsYWNrIiwgZmlsbD0iZ3JleSIsIGFscGhhID0gMC43KSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWFuKGR0X3VzZXJfY2xkJGR1cmF0aW9uKS8zNjUsIGxpbmV0eXBlID0gImRvdHRlZCIsIGNvbG9yID0gInJlZCIsIHNpemUgPSAxLjUpICsKICAgIGxhYnMoeSA9ICJGcmVxdWVuY3kgLyBDb3VudCIsIAogICAgICAgICB4ID0gIk51bWJlciBvZiBZZWFycyBTaW5jZSBSZWdpc3RyYXRpb24iKSArCiAgICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gUGxvdCBvZiBTdXBwb3J0ZXJzJyBSZWdpc3RyYXRpb24gRHVyYXRpb24iKQpgYGAKCiMjIDIuIFN0YXRpc3RpY2FsIE1vZGVsaW5nCgojIyMgMS4gQW5hbHl6aW5nIHRoZSBSZWxhdGlvbnNoaXAgQmV0d2VlbiBGb2xsb3dlcnMgYW5kIG90aGVyIGZhY3RvcnMuIAoKYGBge3J9CiMgU2VsZWN0IHZhcmlhYmxlcyBmb3IgYW5hbHlzaXMuIAp1c2VyX3N0YXQgPC0gZHRfdXNlcl9jbGQgJT4lIAogICAgc2VsZWN0KGZvbGxvd2VycywgZm9sbG93aW5nLCBwdWJsaWNfcmVwb3MsIGR1cmF0aW9uKQoKIyBTYW5pZXR5IENoZWNrCnVzZXJfc3RhdCAlPiUgaGVhZCgxMCkKYGBgCgoKYGBge3J9CiMgVW5zdXBlcnZpc2VkIExlYXJuaW5nOiBQQ0EKdXNlcl9wY2EgPC0gcHJjb21wKHVzZXJfc3RhdCwgY2VudGVyPVRSVUUsIHNjYWxlLj1UUlVFKQpwcmludCh1c2VyX3BjYSkKc3VtbWFyeSh1c2VyX3BjYSkKYGBgCgpgYGB7cn0KIyBTdXBlcnZpc2VkIExlYXJuaW5nOiByZWdyZXNzaW9uCiMgeSA9IGR0X3VzZXJfY2xkJGZvbGxvd2VycwojIHggPSBkdF91c2VyX2NsZCRmb2xsb3dpbmcsIHB1YmxpY19yZXBvcywgZHVyYXRpb24KCmxtIDwtIGxtKGZvbGxvd2VycyB+IGZvbGxvd2luZytwdWJsaWNfcmVwb3MrZHVyYXRpb24sIGRhdGEgPSBkdF91c2VyX2NsZCkKc3VtbWFyeShsbSkKIyBCZWNhdXNlIGFkanVzdGVkIFItc3F1YXJlZCBpcyB2ZXJ5IGxvdywgYXQgMC4wMTUwOC4gV2UgZGVjaWRlZCB0byBmdXJ0aGVyIGNoZWNrIGxpbmVhciBhc3N1bXB0aW9ucyBieSBwbG90dGluZyBpdC4KCnBsb3QobG0pCiMgRnJvbSB0aGUgcGxvdHMsIHdlIGNhbiB0ZWxsIHRoYXQgdGhpcyBkYXRhIHZpb2xhdGVkIHZhcmlvdXMgbGluZWFyIGFzc3VtcHRpb24uIFRoZXJlZm9yZSwgbGluZWFyIHJlZ3Jlc3Npb24gd291bGRuJ3Qgd29yayB3aXRoIHRoaXMgZGF0YXNldC4KYGBgCgoKCiMjIDMuIE1haW4gUXVlc3Rpb25zL0lzc3VlcyBvZiBTdXBwb3J0ZXJzCgojIyMgMS4gVHJlbmRpbmcgSXNzdWVzLiAKCmBgYHtyfQojIFNob3dpbmcgdG9wIHRlbiBpc3N1ZXMgd2l0aCBtb3N0IGNvbW1lbnRzLiAKZHRfaXNzdWVzX2NsZCAlPiUgCiAgICBzZWxlY3QodGl0bGUsIGNvbW1lbnRzKSAlPiUgCiAgICBhcnJhbmdlKGRlc2MoY29tbWVudHMpKSAlPiUKICAgIGhlYWQoMTApCgojIFBzdWVkbyB0b3AgdGVuIGlzc3VlcyAodHJhbnNsYXRlZCkKcHN1ZWRvX2lzc3VlcyA8LSBkYXRhLmZyYW1lKAogICAgdGl0bGUgPSBjKCJEaXNjdXNzaW9uIFRocmVhZCIsIAogICAgICAgICAgICAgICJBbnkgJ1dvcmtpbmcgdW5kZXIgOTk2LCBzaWNraW5nIGluIElDVScgd2FsbHBhcGVycyB0byB1c2U/IiwgCiAgICAgICAgICAgICAgIkFmdGVyd2FyZHMsIEkgY291bGQgcHV0ICdwYXJ0aWNpcGF0ZWQgaW4gYW4gb3Blbi1zb3VyY2UgcHJvamVjdCB3aXRoIG92ZXIgMjAwMCsgc3RhcnMnIG9uIG15IHJlc3VtZSIsIAogICAgICAgICAgICAgICJJIGRvbid0IHVuZGVyc3RhbnQgdGhlIGxhdywgYnV0IEknbSB3b25kZXJpbmcgaWYgdGhlcmUgaXMgYW55IGxlZ2FsIGlzc3VlIGludm9sdmVkPyIsIAogICAgICAgICAgICAgICJDYW4gdGhpcyByZXBvc2l0b3J5IGJlIGluIHRoZSB0b3AtdGVuIHN0YXJzIGxpc3Qgb24gR2l0SHViPyIsIAogICAgICAgICAgICAgICJTdWJzdGFudGlhbCBzdWdnZXN0aW9ucyByZWdhcmRpbmcgdGhlIGFudGktOTk2IG1vdmVtZW50cy4iLCAKICAgICAgICAgICAgICAiSXQncyB1Z2x5IHRoYXQgdGhlIGRldmVsb3BlcnMgdGFraW5nIHNhbGFyaWVzIHdoaWxlIGNvbXBsYWluaW5nIGFib3V0IHRoZWlyIGNvbXBhbmllcy4iLCAKICAgICAgICAgICAgICAiV29ya2luZyBvdmVydGltZSB0b25pZ2h0LCB3aWxsIGRlbGV0ZSB0aGUgZGF0YWJhc2Ugd2hlbiB0aGlzIHJlcG8gcmVhY2ggb3ZlciAxMDBrIHN0YXJzIiwgCiAgICAgICAgICAgICAgIkN1dGUgZ2lybCBib3JuIGluIDE5OTYgaXMgbG9va2luZyBmb3IgZGV2ZWxvcGVyIGJveWZyaWVuZCBub3cuIiwgCiAgICAgICAgICAgICAgIldvcnNoaXAgdGhlIG9yaWdpbmFsIHBvc3QiKSwgCiAgICBjb21tZW50cyA9IGMoMTI0MywgNjIsIDUzLCAzOSwgMzcsIDMwLCAzMCwgMjYsIDI1LCAyNCkpICU+JQogICAgbXV0YXRlKHRpdGxlID0gYXMuY2hhcmFjdGVyKHRpdGxlKSkKCnBzdWVkb19pc3N1ZXMKYGBgCgojIyMgMi4gVGV4dCBBbmFseXNpcyBBYm91dCBUb3BpY3Mgb2YgOTk2IE1vdmVtZW50LgoKYGBge3J9CiMgU2V0dGluZyB0aGUgd29yZC1zcGxpdCBlbmdpbmUKc3BsaXR0ZXIgPC0gd29ya2VyKHN0b3Bfd29yZCA9ICJkYXRhL3N0b3B3b3Jkcy50eHQiKQoKIyBTcGxpdHRpbmcgdGhlIHdvcmRzLiAKc2VnIDwtIGMoc3BsaXR0ZXJbZHRfaXNzdWVzX2NsZCR0aXRsZV0sIHNwbGl0dGVyW2R0X2lzc3Vlc19jbGQkYm9keV0pCnNlZyA8LSBzZWdbbmNoYXIoc2VnKSA+IDFdCiMgRW5jb2RlIHRoZSBjaGluZXNlIHdvcmQgdmVjdG9yIGFzIFVURi04IGZvcm1hdC4gCkVuY29kaW5nKHNlZykgPC0gIlVURi04IgojIEV4dHJhY3QgdGhlIHRvcCAxMDAgCnNlZ19kZiA8LSBkYXRhLmZyYW1lKHNlZyA9IHNlZykgJT4lCiAgICBncm91cF9ieShzZWcpICU+JQogICAgc3VtbWFyaXNlKGZyZXEgPSBuKCkpICU+JQogICAgYXJyYW5nZShkZXNjKGZyZXEpKSAlPiUKICAgIGhlYWQoMTAwKQoKIyBHZW5lcmF0aW5nIHdvcmQgY2xvdWQgKENoaW5lc2UgVmVyc2lvbikuIApmb250X2ZhbWlseSA8LSBwYXIoImZhbWlseSIpCnBhcihmYW1pbHkgPSAiQWRvYmUgSGVpdGkgU3RkIFIiKQp3b3JkY2xvdWQod29yZHM9c2VnX2RmJHNlZywgZnJlcT1zZWdfZGYkZnJlcSwgCiAgICAgICAgICBjb2xvcnM9YnJld2VyLnBhbCg4LCJEYXJrMiIpLCAKICAgICAgICAgIHNjYWxlPWMoNCwgMC44KSkKYGBgCgoKYGBge3J9CiMgTG9hZGluZyB0cmFuc2xhdGVkIGRhdGFzZXQuIAp0cmFucyA8LSByZWFkLnRhYmxlKCJkYXRhL3RyYW5zbGF0ZSIsIHNlcD0iXHQiKVstMSxdCnNlZ19kZiA8LSBjYmluZChzZWdfZGYsIGVuZ2wgPSB0cmFucyRWMikKYGBgCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQojIEdlbmVyYXRpbmcgd29yZCBjbG91ZCAoRW5nbGlzaCBWZXJzaW9uKQp3b3JkY2xvdWQod29yZHM9c2VnX2RmJGVuZ2wsIGZyZXE9c2VnX2RmJGZyZXEsIAogICAgICAgICAgY29sb3JzPWJyZXdlci5wYWwoOCwiRGFyazIiKSwgCiAgICAgICAgICBzY2FsZT1jKDQsIDAuOCkpCmBgYAoKCiMgSVYgLSByZWZlcmVuY2UKIyMjIyBLZW50b24sIFdpbGwuIOKAnFNvY2lhbCBHb29kLuKAnSBJbnZlc3RvcGVkaWEsIEludmVzdG9wZWRpYSwgMTIgTWFyLiAyMDE5LCB3d3cuaW52ZXN0b3BlZGlhLmNvbS90ZXJtcy9zL3NvY2lhbF9nb29kLmFzcC4KCiMjIyMgV2VpLCBTaGl5YW5nLiAiQSBwaWxvdCBzdHVkeSBvbiB0aGUgQ2hpbmVzZSBpbnRlcm5ldCBlbnZpcm9ubWVudC4iIEludGVybmF0aW9uYWwgQ29uZmVyZW5jZSBvbiBBZHZhbmNlcyBpbiBFZHVjYXRpb24gYW5kIE1hbmFnZW1lbnQuIFNwcmluZ2VyLCBCZXJsaW4sIEhlaWRlbGJlcmcsIDIwMTEuCgojIyMjIFl1YW4sIExpLiDigJxMZWFybmluZyBDaGluYSdzIEZvcmJpZGRlbiBIaXN0b3J5LCBTbyBUaGV5IENhbiBDZW5zb3IgSXQu4oCdIFRoZSBOZXcgWW9yayBUaW1lcywgVGhlIE5ldyBZb3JrIFRpbWVzLCAyIEphbi4gMjAxOSwgd3d3Lm55dGltZXMuY29tLzIwMTkvMDEvMDIvYnVzaW5lc3MvY2hpbmEtaW50ZXJuZXQtY2Vuc29yLmh0bWwuCgojIyMjIOKAnENoaW5lc2UgRGV2ZWxvcGVycyBVc2UgR2l0aHViIHRvIFByb3Rlc3QgYWdhaW5zdCBDb3VudHJ5J3MgOTk2IFdvcmsgU2NoZWR1bGUu4oCdIFNvdXRoIENoaW5hIE1vcm5pbmcgUG9zdCwgMjkgTWFyLiAyMDE5LCB3d3cuc2NtcC5jb20vdGVjaC9zdGFydC11cHMvYXJ0aWNsZS8zMDAzNjkxL2RldmVsb3BlcnMtbGl2ZXMtbWF0dGVyLWNoaW5lc2Utc29mdHdhcmUtZW5naW5lZXJzLXVzZS1naXRodWIuCgoKCgoKCgoKCgoKCgoKCgoKCgoK